package erfgame.core.graphics.renderer;

import java.awt.Point;
import java.util.ArrayList;


import erfgame.core.CompassDirection;
import erfgame.core.Rotation;
import erfgame.core.entity.Entity;
import erfgame.core.entity.Orientable;
import erfgame.core.entity.State;
import erfgame.core.entity.Stateful;
import erfgame.core.graphics.renderer.animation.AnimationCycle;
import erfgame.core.graphics.renderer.animation.StateAnimationCycleFactory;
import erfgame.core.graphics.renderer.render.Animation;
import erfgame.core.graphics.renderer.render.EntityAnimationFactory;
import erfgame.core.graphics.renderer.render.Render;
import erfgame.core.world.World;

public class RenderEntityRendererFactoryAdapter implements EntityRendererFactory {

    public static final String GRAB_POINT = "grab-point";
    
    private EntityAnimationFactory renderFactory;
    private StateAnimationCycleFactory animationCycleFactory;
    private int depth;
    
    public RenderEntityRendererFactoryAdapter( EntityAnimationFactory renderFactory, StateAnimationCycleFactory animationCycleFactory, int depth ) {
        this.renderFactory = renderFactory;
        this.animationCycleFactory = animationCycleFactory;
        this.depth = depth;
    }
    
    public EntityRenderer getRenderer( World world, Entity entity, Rotation viewDirection ) {
        State state;
        long startTime;
        float progress;
        long worldTime = world.getWorldTime();
        if( entity instanceof Stateful ) {
            Stateful stateful = (Stateful)entity;
            state = stateful.getCurrentState();
            startTime = stateful.getCurrentStateTime();
        } else {
            state = null;
            progress = 0;
            startTime = worldTime;
        }
        
        // going to be difficult to obtain this (pass it in?)
        CompassDirection orientation;
        if( entity instanceof Orientable ) {
            orientation = ((Orientable)entity).getOrientation();
        } else {
            orientation = null;
        }
        double rotation = 0;
    	return getRenderer( world, entity, state, orientation, rotation, viewDirection );
    }
    
    public EntityRenderer getRenderer(World world, Entity entity, State state, CompassDirection orientation, double rotation, Rotation viewDirection ) {
        // produce an animation of the entity's current state/orientation
        try {
            AnimationCycle animationCycle = animationCycleFactory.getAnimationCycle(state, entity.getEntityType());

            EntityRenderer result;
            
            Animation animation = renderFactory.getAnimation( entity, state, orientation, rotation );
            if( animationCycle != null ) {
                int cycleTime = animationCycle.getCycleTime();
                int[] stepTimes = animationCycle.getStepTimes();
                ArrayList<AnimationFrame> frames = new ArrayList<AnimationFrame>(stepTimes.length);
                int sum = 0;
                for( int i=0; i<stepTimes.length; i++ ) {
                    int stepTime = stepTimes[i];
                    sum += stepTime;
                    float stepProgress = ((float)sum)/((float)cycleTime);
                    Render render = animation.getFrame(stepProgress);
                    RenderEntityRenderer renderRenderer = getRenderer(entity, render);
                    // TODO : kind of messy + dumb, maybe the animated renderer should delegate to the current frame??
                    
                    AnimationFrame frame = new AnimationFrame(stepTime, renderRenderer);
                    frames.add( frame );
                }
                AnimatedEntityRenderer animatedRenderer = new AnimatedEntityRenderer(frames);
                animatedRenderer.setWorld(world);
                animatedRenderer.setEntity(entity);
                // TODO use progress??
//                int duration = animationCycle.getCycleProgress(worldTime - startTime);
//                progress = ((float)duration)/((float)animationCycle.getCycleTime());
                result = animatedRenderer;
            } else {
                Render render = animation.getFrame(0);
                result = getRenderer( entity, render );
            }
            return result;
        } catch( Exception ex ) {
            throw new RuntimeException( "unable to load render of "+entity, ex );
        }
    }

    public RenderEntityRenderer getRenderer( Entity entity, Render render ) {
        Point grabPoint = render.getPoint(GRAB_POINT);
        return new RenderEntityRenderer( render, entity, grabPoint, depth );
        
    }
}
